home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 May / maximum-cd-2009-05.iso / DiscContents / Firefox Setup 3.0.6.exe / nonlocalized / components / nsBlocklistService.js < prev    next >
Encoding:
Text File  |  2009-01-19  |  29.3 KB  |  879 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 41 "e:\fx19rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  4. */
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  11.  
  12. const TOOLKIT_ID                      = "toolkit@mozilla.org"
  13. const KEY_PROFILEDIR                  = "ProfD";
  14. const KEY_APPDIR                      = "XCurProcD";
  15. const FILE_BLOCKLIST                  = "blocklist.xml";
  16. const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
  17. const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
  18. const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
  19. const PREF_GENERAL_USERAGENT_LOCALE   = "general.useragent.locale";
  20. const PREF_PARTNER_BRANCH             = "app.partner.";
  21. const PREF_APP_DISTRIBUTION           = "distribution.id";
  22. const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
  23. const PREF_APP_UPDATE_CHANNEL         = "app.update.channel";
  24. const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
  25. const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
  26. const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
  27. const UNKNOWN_XPCOM_ABI               = "unknownABI";
  28.  
  29. const MODE_RDONLY   = 0x01;
  30. const MODE_WRONLY   = 0x02;
  31. const MODE_CREATE   = 0x08;
  32. const MODE_APPEND   = 0x10;
  33. const MODE_TRUNCATE = 0x20;
  34.  
  35. const PERMS_FILE      = 0644;
  36. const PERMS_DIRECTORY = 0755;
  37.  
  38. var gApp = null;
  39. var gPref = null;
  40. var gOS = null;
  41. var gConsole = null;
  42. var gVersionChecker = null;
  43. var gLoggingEnabled = null;
  44. var gABI = null;
  45. var gOSVersion = null;
  46.  
  47. // shared code for suppressing bad cert dialogs
  48. //@line 40 "e:\fx19rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\shared\src\badCertHandler.js"
  49.  
  50. /**
  51.  * Only allow built-in certs for HTTPS connections.  See bug 340198.
  52.  */
  53. function checkCert(channel) {
  54.   if (!channel.originalURI.schemeIs("https"))  // bypass
  55.     return;
  56.  
  57.   const Ci = Components.interfaces;  
  58.   var cert =
  59.       channel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
  60.       SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
  61.  
  62.   var issuer = cert.issuer;
  63.   while (issuer && !cert.equals(issuer)) {
  64.     cert = issuer;
  65.     issuer = cert.issuer;
  66.   }
  67.  
  68.   if (!issuer || issuer.tokenName != "Builtin Object Token")
  69.     throw "cert issuer is not built-in";
  70. }
  71.  
  72. /**
  73.  * This class implements nsIBadCertListener.  It's job is to prevent "bad cert"
  74.  * security dialogs from being shown to the user.  It is better to simply fail
  75.  * if the certificate is bad. See bug 304286.
  76.  */
  77. function BadCertHandler() {
  78. }
  79. BadCertHandler.prototype = {
  80.  
  81.   // nsIChannelEventSink
  82.   onChannelRedirect: function(oldChannel, newChannel, flags) {
  83.     // make sure the certificate of the old channel checks out before we follow
  84.     // a redirect from it.  See bug 340198.
  85.     checkCert(oldChannel);
  86.   },
  87.  
  88.   // Suppress any certificate errors
  89.   notifyCertProblem: function(socketInfo, status, targetSite) {
  90.     return true;
  91.   },
  92.  
  93.   // Suppress any ssl errors
  94.   notifySSLError: function(socketInfo, error, targetSite) {
  95.     return true;
  96.   },
  97.  
  98.   // nsIInterfaceRequestor
  99.   getInterface: function(iid) {
  100.     return this.QueryInterface(iid);
  101.   },
  102.  
  103.   // nsISupports
  104.   QueryInterface: function(iid) {
  105.     if (!iid.equals(Components.interfaces.nsIChannelEventSink) &&
  106.         !iid.equals(Components.interfaces.nsIBadCertListener2) &&
  107.         !iid.equals(Components.interfaces.nsISSLErrorListener) &&
  108.         !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
  109.         !iid.equals(Components.interfaces.nsISupports))
  110.       throw Components.results.NS_ERROR_NO_INTERFACE;
  111.     return this;
  112.   }
  113. };
  114. //@line 86 "e:\fx19rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  115.  
  116. /**
  117.  * Logs a string to the error console.
  118.  * @param   string
  119.  *          The string to write to the error console..
  120.  */
  121. function LOG(string) {
  122.   if (gLoggingEnabled) {
  123.     dump("*** " + string + "\n");
  124.     if (gConsole)
  125.       gConsole.logStringMessage(string);
  126.   }
  127. }
  128.  
  129. /**
  130.  * Gets a preference value, handling the case where there is no default.
  131.  * @param   func
  132.  *          The name of the preference function to call, on nsIPrefBranch
  133.  * @param   preference
  134.  *          The name of the preference
  135.  * @param   defaultValue
  136.  *          The default value to return in the event the preference has
  137.  *          no setting
  138.  * @returns The value of the preference, or undefined if there was no
  139.  *          user or default value.
  140.  */
  141. function getPref(func, preference, defaultValue) {
  142.   try {
  143.     return gPref[func](preference);
  144.   }
  145.   catch (e) {
  146.   }
  147.   return defaultValue;
  148. }
  149.  
  150. /**
  151.  * Gets the file at the specified hierarchy under a Directory Service key.
  152.  * @param   key
  153.  *          The Directory Service Key to start from
  154.  * @param   pathArray
  155.  *          An array of path components to locate beneath the directory
  156.  *          specified by |key|. The last item in this array must be the
  157.  *          leaf name of a file.
  158.  * @return  nsIFile object for the file specified. The file is NOT created
  159.  *          if it does not exist, however all required directories along
  160.  *          the way are.
  161.  */
  162. function getFile(key, pathArray) {
  163.   var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
  164.                     getService(Ci.nsIProperties);
  165.   var file = fileLocator.get(key, Ci.nsILocalFile);
  166.   for (var i = 0; i < pathArray.length - 1; ++i) {
  167.     file.append(pathArray[i]);
  168.     if (!file.exists())
  169.       file.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
  170.   }
  171.   file.followLinks = false;
  172.   file.append(pathArray[pathArray.length - 1]);
  173.   return file;
  174. }
  175.  
  176. /**
  177.  * Opens a safe file output stream for writing.
  178.  * @param   file
  179.  *          The file to write to.
  180.  * @param   modeFlags
  181.  *          (optional) File open flags. Can be undefined.
  182.  * @returns nsIFileOutputStream to write to.
  183.  */
  184. function openSafeFileOutputStream(file, modeFlags) {
  185.   var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
  186.             createInstance(Ci.nsIFileOutputStream);
  187.   if (modeFlags === undefined)
  188.     modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
  189.   if (!file.exists())
  190.     file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
  191.   fos.init(file, modeFlags, PERMS_FILE, 0);
  192.   return fos;
  193. }
  194.  
  195. /**
  196.  * Closes a safe file output stream.
  197.  * @param   stream
  198.  *          The stream to close.
  199.  */
  200. function closeSafeFileOutputStream(stream) {
  201.   if (stream instanceof Ci.nsISafeOutputStream)
  202.     stream.finish();
  203.   else
  204.     stream.close();
  205. }
  206.  
  207. /**
  208.  * Constructs a URI to a spec.
  209.  * @param   spec
  210.  *          The spec to construct a URI to
  211.  * @returns The nsIURI constructed.
  212.  */
  213. function newURI(spec) {
  214.   var ioServ = Cc["@mozilla.org/network/io-service;1"].
  215.                getService(Ci.nsIIOService);
  216.   return ioServ.newURI(spec, null, null);
  217. }
  218.  
  219. /**
  220.  * Checks whether this blocklist element is valid for the current OS and ABI.
  221.  * If the element has an "os" attribute then the current OS must appear in
  222.  * it's comma separated list for the element to be valid. Similarly for the
  223.  * xpcomabi attribute.
  224.  */
  225. function matchesOSABI(blocklistElement) {
  226.   if (blocklistElement.hasAttribute("os")) {
  227.     var choices = blocklistElement.getAttribute("os").split(",");
  228.     if (choices.length > 0 && choices.indexOf(gApp.OS) < 0)
  229.       return false;
  230.   }
  231.   
  232.   if (blocklistElement.hasAttribute("xpcomabi")) {
  233.     choices = blocklistElement.getAttribute("xpcomabi").split(",");
  234.     if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0)
  235.       return false;
  236.   }
  237.   
  238.   return true;
  239. }
  240.  
  241. /**
  242.  * Gets the current value of the locale.  It's possible for this preference to
  243.  * be localized, so we have to do a little extra work here.  Similar code
  244.  * exists in nsHttpHandler.cpp when building the UA string.
  245.  */
  246. function getLocale() {
  247.   try {
  248.       // Get the default branch
  249.       var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  250.           .getService(Components.interfaces.nsIPrefService);
  251.       var defaultPrefs = prefs.getDefaultBranch(null);
  252.       return defaultPrefs.getCharPref(PREF_GENERAL_USERAGENT_LOCALE);
  253.   } catch (e) {}
  254.  
  255.   return gPref.getCharPref(PREF_GENERAL_USERAGENT_LOCALE);
  256. }
  257.  
  258. /**
  259.  * Read the update channel from defaults only.  We do this to ensure that
  260.  * the channel is tightly coupled with the application and does not apply
  261.  * to other installations of the application that may use the same profile.
  262.  */
  263. function getUpdateChannel() {
  264.   var channel = "default";
  265.   var prefName;
  266.   var prefValue;
  267.  
  268.   var defaults =
  269.       gPref.QueryInterface(Components.interfaces.nsIPrefService).
  270.       getDefaultBranch(null);
  271.   try {
  272.     channel = defaults.getCharPref(PREF_APP_UPDATE_CHANNEL);
  273.   } catch (e) {
  274.     // use default when pref not found
  275.   }
  276.  
  277.   try {
  278.     var partners = gPref.getChildList(PREF_PARTNER_BRANCH, { });
  279.     if (partners.length) {
  280.       channel += "-cck";
  281.       partners.sort();
  282.  
  283.       for each (prefName in partners) {
  284.         prefValue = gPref.getCharPref(prefName);
  285.         channel += "-" + prefValue;
  286.       }
  287.     }
  288.   }
  289.   catch (e) {
  290.     Components.utils.reportError(e);
  291.   }
  292.  
  293.   return channel;
  294. }
  295.  
  296. /* Get the distribution pref values, from defaults only */
  297. function getDistributionPrefValue(aPrefName) {
  298.   var prefValue = "default";
  299.  
  300.   var defaults =
  301.       gPref.QueryInterface(Components.interfaces.nsIPrefService).
  302.       getDefaultBranch(null);
  303.   try {
  304.     prefValue = defaults.getCharPref(aPrefName);
  305.   } catch (e) {
  306.     // use default when pref not found
  307.   }
  308.  
  309.   return prefValue;
  310. }
  311.  
  312. /**
  313.  * Manages the Blocklist. The Blocklist is a representation of the contents of
  314.  * blocklist.xml and allows us to remotely disable / re-enable blocklisted
  315.  * items managed by the Extension Manager with an item's appDisabled property.
  316.  * It also blocklists plugins with data from blocklist.xml.
  317.  */
  318.  
  319. function Blocklist() {
  320.   gApp = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
  321.   gApp.QueryInterface(Ci.nsIXULRuntime);
  322.   gPref = Cc["@mozilla.org/preferences-service;1"].
  323.           getService(Ci.nsIPrefBranch2);
  324.   gVersionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"].
  325.                     getService(Ci.nsIVersionComparator);
  326.   gConsole = Cc["@mozilla.org/consoleservice;1"].
  327.              getService(Ci.nsIConsoleService);
  328.  
  329.   gOS = Cc["@mozilla.org/observer-service;1"].
  330.         getService(Ci.nsIObserverService);
  331.   gOS.addObserver(this, "xpcom-shutdown", false);
  332.  
  333.   // Not all builds have a known ABI
  334.   try {
  335.     gABI = gApp.XPCOMABI;
  336.   }
  337.   catch (e) {
  338.     LOG("Blocklist: XPCOM ABI unknown.");
  339.     gABI = UNKNOWN_XPCOM_ABI;
  340.   }
  341.  
  342.   var osVersion;
  343.   var sysInfo = Components.classes["@mozilla.org/system-info;1"]
  344.                           .getService(Components.interfaces.nsIPropertyBag2);
  345.   try {
  346.     osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
  347.   }
  348.   catch (e) {
  349.     LOG("Blocklist: OS Version unknown.");
  350.   }
  351.  
  352.   if (osVersion) {
  353.     try {
  354.       osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
  355.     }
  356.     catch (e) {
  357.       // Not all platforms have a secondary widget library, so an error is nothing to worry about.
  358.     }
  359.     gOSVersion = encodeURIComponent(osVersion);
  360.   }
  361.  
  362. //@line 342 "e:\fx19rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  363. }
  364.  
  365. Blocklist.prototype = {
  366.   /**
  367.    * Extension ID -> array of Version Ranges
  368.    * Each value in the version range array is a JS Object that has the
  369.    * following properties:
  370.    *   "minVersion"  The minimum version in a version range (default = 0)
  371.    *   "maxVersion"  The maximum version in a version range (default = *)
  372.    *   "targetApps"  Application ID -> array of Version Ranges
  373.    *                 (default = current application ID)
  374.    *                 Each value in the version range array is a JS Object that
  375.    *                 has the following properties:
  376.    *                   "minVersion"  The minimum version in a version range
  377.    *                                 (default = 0)
  378.    *                   "maxVersion"  The maximum version in a version range
  379.    *                                 (default = *)
  380.    */
  381.   _addonEntries: null,
  382.   _pluginEntries: null,
  383.  
  384.   observe: function (aSubject, aTopic, aData) {
  385.     switch (aTopic) {
  386.     case "app-startup":
  387.       gOS.addObserver(this, "plugins-list-updated", false);
  388.       gOS.addObserver(this, "profile-after-change", false);
  389.       gOS.addObserver(this, "quit-application", false);
  390.       break;
  391.     case "profile-after-change":
  392.       gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
  393.       var tm = Cc["@mozilla.org/updates/timer-manager;1"].
  394.                getService(Ci.nsIUpdateTimerManager);
  395.       var interval = getPref("getIntPref", PREF_BLOCKLIST_INTERVAL, 86400);
  396.       tm.registerTimer("blocklist-background-update-timer", this, interval);
  397.       break;
  398.     case "plugins-list-updated":
  399.       this._checkPluginsList();
  400.       break;
  401.     case "quit-application":
  402.       gOS.removeObserver(this, "plugins-list-updated");
  403.       gOS.removeObserver(this, "profile-after-change");
  404.       gOS.removeObserver(this, "quit-application");
  405.       break;
  406.     case "xpcom-shutdown":
  407.       gOS.removeObserver(this, "xpcom-shutdown");
  408.       gOS = null;
  409.       gPref = null;
  410.       gConsole = null;
  411.       gVersionChecker = null;
  412.       gApp = null;
  413.       break;
  414.     }
  415.   },
  416.  
  417.   isAddonBlocklisted: function(id, version, appVersion, toolkitVersion) {
  418.     if (!this._addonEntries)
  419.       this._loadBlocklist();
  420.     if (!appVersion)
  421.       appVersion = gApp.version;
  422.     if (!toolkitVersion)
  423.       toolkitVersion = gApp.platformVersion;
  424.  
  425.     var blItem = this._addonEntries[id];
  426.     if (!blItem)
  427.       return false;
  428.  
  429.     for (var i = 0; i < blItem.length; ++i) {
  430.       if (blItem[i].includesItem(version, appVersion, toolkitVersion))
  431.         return true;
  432.     }
  433.     return false;
  434.   },
  435.  
  436.   notify: function(aTimer) {
  437.     if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false)
  438.       return;
  439.  
  440.     try {
  441.       var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
  442.     }
  443.     catch (e) {
  444.       LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
  445.           " is missing!");
  446.       return;
  447.     }
  448.  
  449.     dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
  450.     dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
  451.     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
  452.     dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
  453.     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
  454.     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
  455.     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
  456.     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
  457.     dsURI = dsURI.replace(/%CHANNEL%/g, getUpdateChannel());
  458.     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
  459.     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
  460.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
  461.     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
  462.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
  463.     dsURI = dsURI.replace(/\+/g, "%2B");
  464.  
  465.     // Verify that the URI is valid
  466.     try {
  467.       var uri = newURI(dsURI);
  468.     }
  469.     catch (e) {
  470.       LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
  471.           "for: " + dsURI + ", error: " + e);
  472.       return;
  473.     }
  474.  
  475.     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  476.                   createInstance(Ci.nsIXMLHttpRequest);
  477.     request.open("GET", uri.spec, true);
  478.     request.channel.notificationCallbacks = new BadCertHandler();
  479.     request.overrideMimeType("text/xml");
  480.     request.setRequestHeader("Cache-Control", "no-cache");
  481.     request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  482.  
  483.     var self = this;
  484.     request.onerror = function(event) { self.onXMLError(event); };
  485.     request.onload  = function(event) { self.onXMLLoad(event);  };
  486.     request.send(null);
  487.   },
  488.  
  489.   onXMLLoad: function(aEvent) {
  490.     var request = aEvent.target;
  491.     try {
  492.       checkCert(request.channel);
  493.     }
  494.     catch (e) {
  495.       LOG("Blocklist::onXMLLoad: " + e);
  496.       return;
  497.     }
  498.     var responseXML = request.responseXML;
  499.     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  500.         (request.status != 200 && request.status != 0)) {
  501.       LOG("Blocklist::onXMLLoad: there was an error during load");
  502.       return;
  503.     }
  504.     var blocklistFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
  505.     if (blocklistFile.exists())
  506.       blocklistFile.remove(false);
  507.     var fos = openSafeFileOutputStream(blocklistFile);
  508.     fos.write(request.responseText, request.responseText.length);
  509.     closeSafeFileOutputStream(fos);
  510.     this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
  511.     var em = Cc["@mozilla.org/extensions/manager;1"].
  512.              getService(Ci.nsIExtensionManager);
  513.     em.checkForBlocklistChanges();
  514.     this._checkPluginsList();
  515.   },
  516.  
  517.   onXMLError: function(aEvent) {
  518.     try {
  519.       var request = aEvent.target;
  520.       // the following may throw (e.g. a local file or timeout)
  521.       var status = request.status;
  522.     }
  523.     catch (e) {
  524.       request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
  525.       status = request.status;
  526.     }
  527.     var statusText = request.statusText;
  528.     // When status is 0 we don't have a valid channel.
  529.     if (status == 0)
  530.       statusText = "nsIXMLHttpRequest channel unavailable";
  531.     LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
  532.         statusText);
  533.   },
  534.  
  535.   /**
  536.    * Finds the newest blocklist file from the application and the profile and
  537.    * load it or does nothing if neither exist.
  538.    */
  539.   _loadBlocklist: function() {
  540.     this._addonEntries = { };
  541.     this._pluginEntries = { };
  542.     var profFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
  543.     if (profFile.exists()) {
  544.       this._loadBlocklistFromFile(profFile);
  545.       return;
  546.     }
  547.     var appFile = getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
  548.     if (appFile.exists()) {
  549.       this._loadBlocklistFromFile(appFile);
  550.       return;
  551.     }
  552.     LOG("Blocklist::_loadBlocklist: no XML File found");
  553.   },
  554.  
  555.   /**
  556. //@line 584 "e:\fx19rel\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsBlocklistService.js"
  557.    */
  558.  
  559.   _loadBlocklistFromFile: function(file) {
  560.     if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false) {
  561.       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
  562.       return;
  563.     }
  564.  
  565.     if (!file.exists()) {
  566.       LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
  567.       return;
  568.     }
  569.  
  570.     var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
  571.                                .createInstance(Components.interfaces.nsIFileInputStream);
  572.     fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
  573.     try {
  574.       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
  575.                    createInstance(Ci.nsIDOMParser);
  576.       var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
  577.       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
  578.         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
  579.             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
  580.             "Received: " + doc.documentElement.namespaceURI);
  581.         return;
  582.       }
  583.  
  584.       var childNodes = doc.documentElement.childNodes;
  585.       this._addonEntries = this._processItemNodes(childNodes, "em",
  586.                                                   this._handleEmItemNode);
  587.       this._pluginEntries = this._processItemNodes(childNodes, "plugin",
  588.                                                    this._handlePluginItemNode);
  589.     }
  590.     catch (e) {
  591.       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
  592.       return;
  593.     }
  594.     fileStream.close();
  595.   },
  596.  
  597.   _processItemNodes: function(deChildNodes, prefix, handler) {
  598.     var result = [];
  599.     var itemNodes;
  600.     var containerName = prefix + "Items";
  601.     for (var i = 0; i < deChildNodes.length; ++i) {
  602.       var emItemsElement = deChildNodes.item(i);
  603.       if (emItemsElement instanceof Ci.nsIDOMElement &&
  604.           emItemsElement.localName == containerName) {
  605.         itemNodes = emItemsElement.childNodes;
  606.         break;
  607.       }
  608.     }
  609.     if (!itemNodes)
  610.       return result;
  611.  
  612.     var itemName = prefix + "Item";
  613.     for (var i = 0; i < itemNodes.length; ++i) {
  614.       var blocklistElement = itemNodes.item(i);
  615.       if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
  616.           blocklistElement.localName != itemName)
  617.         continue;
  618.  
  619.       handler(blocklistElement, result);
  620.     }
  621.     return result;
  622.   },
  623.  
  624.   _handleEmItemNode: function(blocklistElement, result) {
  625.     if (!matchesOSABI(blocklistElement))
  626.       return;
  627.  
  628.     var versionNodes = blocklistElement.childNodes;
  629.     var id = blocklistElement.getAttribute("id");
  630.     result[id] = [];
  631.     for (var x = 0; x < versionNodes.length; ++x) {
  632.       var versionRangeElement = versionNodes.item(x);
  633.       if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
  634.           versionRangeElement.localName != "versionRange")
  635.         continue;
  636.  
  637.       result[id].push(new BlocklistItemData(versionRangeElement));
  638.     }
  639.     // if only the extension ID is specified block all versions of the
  640.     // extension for the current application.
  641.     if (result[id].length == 0)
  642.       result[id].push(new BlocklistItemData(null));
  643.   },
  644.  
  645.   _handlePluginItemNode: function(blocklistElement, result) {
  646.     if (!matchesOSABI(blocklistElement))
  647.       return;
  648.  
  649.     var matchNodes = blocklistElement.childNodes;
  650.     var blockEntry = {
  651.       matches: {},
  652.       versions: []
  653.     };
  654.     for (var x = 0; x < matchNodes.length; ++x) {
  655.       var matchElement = matchNodes.item(x);
  656.       if (!(matchElement instanceof Ci.nsIDOMElement))
  657.         continue;
  658.       if (matchElement.localName == "match") {
  659.         var name = matchElement.getAttribute("name");
  660.         var exp = matchElement.getAttribute("exp");
  661.         blockEntry.matches[name] = new RegExp(exp, "m");
  662.       }
  663.       if (matchElement.localName == "versionRange")
  664.         blockEntry.versions.push(new BlocklistItemData(matchElement));
  665.     }
  666.     result.push(blockEntry);
  667.   },
  668.  
  669.   _isPluginBlocklisted: function(plugin, appVersion, toolkitVersion) {
  670.     for each (var blockEntry in this._pluginEntries) {
  671.       var matchFailed = false;
  672.       for (var name in blockEntry.matches) {
  673.         if (!(name in plugin) ||
  674.             typeof(plugin[name]) != "string" ||
  675.             !blockEntry.matches[name].test(plugin[name])) {
  676.           matchFailed = true;
  677.           break;
  678.         }
  679.       }
  680.  
  681.       if (matchFailed)
  682.         continue;
  683.  
  684.       // No version ranges means match any versions
  685.       if (blockEntry.versions.length == 0)
  686.         return true;
  687.  
  688.       for (var i = 0; i < blockEntry.versions.length; i++) {
  689.         if (blockEntry.versions[i].includesItem(plugin.version, appVersion,
  690.                                                 toolkitVersion))
  691.           return true;
  692.       }
  693.     }
  694.  
  695.     return false;
  696.   },
  697.  
  698.   _checkPluginsList: function() {
  699.     if (!this._addonEntries)
  700.       this._loadBlocklist();
  701.     var phs = Cc["@mozilla.org/plugin/host;1"].
  702.               getService(Ci.nsIPluginHost);
  703.     var plugins = phs.getPluginTags({});
  704.     for (var i = 0; i < plugins.length; i++)
  705.       plugins[i].blocklisted = this._isPluginBlocklisted(plugins[i],
  706.                                                          gApp.version,
  707.                                                          gApp.platformVersion);
  708.   },
  709.  
  710.   classDescription: "Blocklist Service",
  711.   contractID: "@mozilla.org/extensions/blocklist;1",
  712.   classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
  713.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  714.                                          Ci.nsIBlocklistService,
  715.                                          Ci.nsITimerCallback]),
  716.   _xpcom_categories: [{
  717.     category: "app-startup",
  718.     service: true
  719.   }]
  720. };
  721.  
  722. /**
  723.  * Helper for constructing a blocklist.
  724.  */
  725. function BlocklistItemData(versionRangeElement) {
  726.   var versionRange = this.getBlocklistVersionRange(versionRangeElement);
  727.   this.minVersion = versionRange.minVersion;
  728.   this.maxVersion = versionRange.maxVersion;
  729.   this.targetApps = { };
  730.   var found = false;
  731.  
  732.   if (versionRangeElement) {
  733.     for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
  734.       var targetAppElement = versionRangeElement.childNodes.item(i);
  735.       if (!(targetAppElement instanceof Ci.nsIDOMElement) ||
  736.           targetAppElement.localName != "targetApplication")
  737.         continue;
  738.       found = true;
  739.       // default to the current application if id is not provided.
  740.       var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
  741.       this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
  742.     }
  743.   }
  744.   // Default to all versions of the current application when no targetApplication
  745.   // elements were found
  746.   if (!found)
  747.     this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
  748. }
  749.  
  750. BlocklistItemData.prototype = {
  751.   /**
  752.    * Tests if a version of an item is included in the version range and target
  753.    * application information represented by this BlocklistItemData using the
  754.    * provided application and toolkit versions.
  755.    * @param   version
  756.    *          The version of the item being tested.
  757.    * @param   appVersion
  758.    *          The application version to test with.
  759.    * @param   toolkitVersion
  760.    *          The toolkit version to test with.
  761.    * @returns True if the version range covers the item version and application
  762.    *          or toolkit version.
  763.    */
  764.   includesItem: function(version, appVersion, toolkitVersion) {
  765.     // Some platforms have no version for plugins, these don't match if there
  766.     // was a min/maxVersion provided
  767.     if (!version && (this.minVersion || this.maxVersion))
  768.       return false;
  769.  
  770.     // Check if the item version matches
  771.     if (!this.matchesRange(version, this.minVersion, this.maxVersion))
  772.       return false;
  773.  
  774.     // Check if the application version matches
  775.     if (this.matchesTargetRange(gApp.ID, appVersion))
  776.       return true;
  777.  
  778.     // Check if the toolkit version matches
  779.     return this.matchesTargetRange(TOOLKIT_ID, toolkitVersion);
  780.   },
  781.  
  782.   /**
  783.    * Checks if a version is higher than or equal to the minVersion (if provided)
  784.    * and lower than or equal to the maxVersion (if provided).
  785.    * @param   version
  786.    *          The version to test.
  787.    * @param   minVersion
  788.    *          The minimum version. If null it is assumed that version is always
  789.    *          larger.
  790.    * @param   maxVersion
  791.    *          The maximum version. If null it is assumed that version is always
  792.    *          smaller.
  793.    */
  794.   matchesRange: function(version, minVersion, maxVersion) {
  795.     if (minVersion && gVersionChecker.compare(version, minVersion) < 0)
  796.       return false;
  797.     if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0)
  798.       return false;
  799.     return true;
  800.   },
  801.  
  802.   /**
  803.    * Tests if there is a matching range for the given target application id and
  804.    * version.
  805.    * @param   appID
  806.    *          The application ID to test for, may be for an application or toolkit
  807.    * @param   appVersion
  808.    *          The version of the application to test for.
  809.    * @returns True if this version range covers the application version given.
  810.    */
  811.   matchesTargetRange: function(appID, appVersion) {
  812.     var blTargetApp = this.targetApps[appID];
  813.     if (!blTargetApp)
  814.       return false;
  815.  
  816.     for (var x = 0; x < blTargetApp.length; ++x) {
  817.       if (this.matchesRange(appVersion, blTargetApp[x].minVersion, blTargetApp[x].maxVersion))
  818.         return true;
  819.     }
  820.  
  821.     return false;
  822.   },
  823.  
  824.   /**
  825.    * Retrieves a version range (e.g. minVersion and maxVersion) for a
  826.    * blocklist item's targetApplication element.
  827.    * @param   targetAppElement
  828.    *          A targetApplication blocklist element.
  829.    * @returns An array of JS objects with the following properties:
  830.    *          "minVersion"  The minimum version in a version range (default = null).
  831.    *          "maxVersion"  The maximum version in a version range (default = null).
  832.    */
  833.   getBlocklistAppVersions: function(targetAppElement) {
  834.     var appVersions = [ ];
  835.  
  836.     if (targetAppElement) {
  837.       for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
  838.         var versionRangeElement = targetAppElement.childNodes.item(i);
  839.         if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
  840.             versionRangeElement.localName != "versionRange")
  841.           continue;
  842.         appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
  843.       }
  844.     }
  845.     // return minVersion = null and maxVersion = null if no specific versionRange
  846.     // elements were found
  847.     if (appVersions.length == 0)
  848.       appVersions.push(this.getBlocklistVersionRange(null));
  849.     return appVersions;
  850.   },
  851.  
  852.   /**
  853.    * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
  854.    * versionRange element.
  855.    * @param   versionRangeElement
  856.    *          The versionRange blocklist element.
  857.    * @returns A JS object with the following properties:
  858.    *          "minVersion"  The minimum version in a version range (default = null).
  859.    *          "maxVersion"  The maximum version in a version range (default = null).
  860.    */
  861.   getBlocklistVersionRange: function(versionRangeElement) {
  862.     var minVersion = null;
  863.     var maxVersion = null;
  864.     if (!versionRangeElement)
  865.       return { minVersion: minVersion, maxVersion: maxVersion };
  866.  
  867.     if (versionRangeElement.hasAttribute("minVersion"))
  868.       minVersion = versionRangeElement.getAttribute("minVersion");
  869.     if (versionRangeElement.hasAttribute("maxVersion"))
  870.       maxVersion = versionRangeElement.getAttribute("maxVersion");
  871.  
  872.     return { minVersion: minVersion, maxVersion: maxVersion };
  873.   }
  874. };
  875.  
  876. function NSGetModule(aCompMgr, aFileSpec) {
  877.   return XPCOMUtils.generateModule([Blocklist]);
  878. }
  879.